package com.sky.up.u_tcp;

public class TCPDemo {

}
/**
# TCP那些事儿

## 七层模型
	* TCP在网络OSI的七层模型中的第四层 -- Transport层，数据叫Segment
	* IP在第三层 -- Network层,数据叫Packet
	* ARP在第二层 -- Data Link层，第二层的数据叫Frame
	* 程序数据首先会打到TCP的Segment中，然后TCP的Segment会打到IP的Packet中，
	然后再打到以太网Ethernet的Frame中，传到对端后，各个层解析自己的协议，然后把数据
	交给更高层的协议处理
	
## TCP头格式
	* TCP的包是没有IP地址的，那是IP层上的事，但是有源端口和目标端口
	* 一个TCP连接需要四个元组件来标识同一个连接(src_ip, src_port, dst_ip, dst_port)
	* Sequence Number是包的序号，用来解决网络包乱序(reordering)问题
	* Acknowledgement Number就是ACK -- 用于确认收到，用来解决不丢包的问题
	* Window又叫Advertised-Window，也就是滑动窗口(Sliding Window)，用于解决流控的
	* TCP Flag，包的类型，主要用于控制TCP的状态机

## TCP的状态机
	* 网络上的传输是没有连接的，TCP也一样
	* TCP所谓的“连接”，是在通讯双方维护的一个“连接状态”
	* TCP协议的状态机：http://www.tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF-2.htm

## 建链接的3次握手和断链接的4次挥手
	对于建链接的3次握手，主要是初始化Sequence Number的初始值。通讯双方要相互通知对方自己的初始化的
Sequence Number(缩写ISN：Inital Sequence Number) -- SYN,Synchronize Sequence Numbers
这个号要作为以后的数据通讯的序号，以保证应用层接收到的数据不会因为网络上的传输的问题而乱序(TCP会用这个序号来拼接数据)
	对于4次挥手，其实是2次，因为TCP是全双工的，所以，发送方和接收方都需要Fin和Ack。只不过，有一方是被动的。如果
两边同时断连接，就会进入到CLOSING状态，然后到达TIME_WAIT状态

## 注意事项
	* 关于建连接时SYN超时。如果server端接到client发的SYN后回了SYN-ACK后client掉线了，server端没有收到client
回来的ACK，那么，这个连接处于一个中间状态，即没成功，也没失败。于是，server端如果在一定时间内没有收到的TCP会重发SYN-ACK.
在Linux下，默认重试次数为5次，重试的间隔时间从1s开始每次都翻番，5次的重试时间间隔为1s,2s,4s,8s,16s，总共31s，第5次
发出后还要等32s都知道第五次也超时了，所以总共需要1s+2s+4s+8s+16s+32s = 2^6-1 = 63s，TCP才会断开这个连接
	* 关于SYN Flood攻击。一些恶意的人就为此制造了SYN Flood攻击 -- 给服务器发了一个SYN后，就下线了，于是服务器需要默认63s
才会断开连接，这样攻击者就可以把服务器的syn连接队列耗尽，让正常的连接请求不能处理。于是Linux给一个叫tcp_syncookies的
参数来应对这个事 -- 当SYN队列满了之后，TCP会通过源地址端口、目标地址端口和时间戳打造一个特别的Sequence Number发出去
(cookie)，如果是攻击者则不会有响应，如果是正常连接，则会把这个SYN Cookie发回来，然后服务端可以通过cookie建连接(即使
你不在SYN队列中)。请注意，请千万先别用tcp_syncookie来处理正常的大负载的连接情况。因为syncookies是妥协版的TCP协议，
并不严谨。对于正常的请求，你应该调整三个可供你选择的TCP参数，第一个是：tcp_synack_retries 调整重试次数，第二个是：
tcp_max_syn_backlog调整SYN连接数，第三个是：tcp_abort_on_overflow拒绝处理不来的连接
	* 关于ISN的初始化。ISN是不能hard code，不然会出现问题--比如：如果一个连接建立好后始终用1来做ISN，如果client发了
30个segment过去，但是网络断了，于是client重连，又用了1做ISN，但是之前连接的那些包到了，于是就被当成了新的包。此时，client
的Sequence Number可能是3，而Server端认为client端的这个号是30了。全乱了。RFC793中说，ISN会和一个假的时钟绑在一起，
这个时钟会在每4微秒对ISN做加1操作，知道超过2^32，又从0开始。这样，一个ISN的周期大约是4.55个小时。因为，假设TCP Segment在网络
上的存活时间不会超过Maximum Segment Lifetime(缩写为MSL)，所以，只要MSL的值小与4.55小时，就不会重用到ISN。
	* 关于MSL和TIME_WAIT。在TCP的状态图中，从TIME_WAIT状态到CLOSED状态，有一个超时设置，这个超时设置是2*MSL
(RFC793定义了MSL为2分钟，Linux设置成了30s)。
		-- > 为什么要有这个TIME_WAIT?为什么不直接转成CLOSED状态呢？
		主要原因：
			1）TIME_WAIT确保有足够的时间让对端收到ACK，如果被动关闭的那方没有收到ACK，就会触发被动端重发FIN，一来
		一去正好2个MSL 
			2）有足够的时间让这个连接不会跟后面的连接混在一起（有些路由器会缓存IP数据包），如果连接被重用了，那么延迟的连接就
		有可能会跟新连接混在一起
			http://www.serverframework.com/asynchronousevents/2011/01/time-wait-and-its-design-implications-for-protocols-and-scalable-servers.html
	* 关于TIME_WAIT数量太多。TIME_WAIT是个很重要的状态，但是如果在大并发的短连接下，TIME_WAIT就会太多，会消耗很多系统资源。
网上搜下，你就会发现，十有八九的处理方式都是教你设置两个参数，一个叫tcp_tw_reuse，另一个叫tcp_tw_recycle的参数，这两个参数默认
是被关闭的，后者recycle比前者reuse更为激进。另外使用tcp_tw_reuse，必须设置tcp_timestamps=1，否则无效。这里，你一定要注意，
打开这两个参数会有比较大的坑 -- 可能会让TCP连接出现一些诡异的问题(因为如上述一样，如果不等待超时重用连接的话，新的连接可能会建不上)
		* 关于tcp_tw_reuse。tcp_tw_reuse加上tcp_timestamps(又叫PAWS，for Protection Against Wrapped Sequence
		Numbers)，可以保证协议的角度上的安全，但是你需要tcp_timestamps在两边都被打开
		* 关于tcp_tw_recycle。如果是tcp_tw_recycle被打开，会假设对端开启tcp_timestamps，然后会去比较时间戳，如果时间戳
		变大了，就可以重用。但是，如果对端是一个NAT网络(如：一个公司只用一个IP出公网)或是对端的IP被另一台重用了，这个事情就复杂了。
		建链接的SYN可能就被直接丢掉了(你可能看到connection time out的错误)
		* 关于tcp_max_tw_buckets。这个是控制并发的TIME_WAIT的数量，默认值是18000，如果超限，那么，系统会把多的给destroy，
		然后在日志里打一个警告(如：time wait bucket table overflow),官方文档说这个参数是用来对抗DOS攻击的。也就是说默认值
		18000并不小。这个还是需要根据实际情况考虑。
		* 使用tcp_tw_reuse和tcp_tw_recycle来解决TIME_WAIT的问题是非常非常危险的，因为这两个参数违反了TCP协议(RFC 1122)
		* TIME_WAIT表示你主动断开连接，如果让对端断开连接，这个问题就是对端的。另外，如果你的服务器是HTTP服务器，那么设置一个HTTP的keepAlive
		有多重要(浏览器会重用一个TCP连接来处理多个HTTP请求)，然后让客户端去断开连接(浏览器可能会不到万不得已不会主动断开连接)

## 数据传输中的Sequence Number
	如果使用Wireshark抓包程序看3次握手，你会发现SeqNum总是0，不是这样的，Wireshark为了显示
更友好，使用了Ralative SeqNum -- 相对序号，你只要在右键菜单中的protocol preference中取消掉
就可以看到"Absoulte SeqNum"了

## TCP重传机制
	TCP要保证所有的数据包都可以到达，所以，必须要有重传机制
	注意，接收端给发送端的Ack确认只会确认最后一个连续的包。比如，发送端发了1,2,3,4,5一共5份数据，
接收端收到1，2，于是回Ack 3,然后收到4(此时3没有收到)，此时TCP会怎么办？SeqNum和Ack是以字节数为单位，
所以ack的时候，不能调着确认，只能确认最大的连续收到的包，不然发送端以为之前的都收到了

## 超时重传机制
	一种是不回ack，死等3，当发送方发现收不到3的ack超时后，会重传3.一旦接收方收到3后，会ack回4 --意味着3和4都收到了
	但是，这种方式会有比较严重的问题，那就是因为要死等3，所以会导致4和5即便已经收到了，而发送方也完全不知道发生了
什么事，因为没有收到ACK，所以发送方悲观地认为也丢了，所以有可能也会导致4和5的重传
	对此有两种选择：
		一种是仅重传timeout的包，也就是第3份数据
		另一种是重传timeout后所有的数据，也就是第3、4、5这三份数据。
	这两种方式都不好，第一种会节省带宽，但是慢；第二种会快一点，但是会浪费带宽，也可能会有无用功。因为都在等timeout，
timeout可能会很长。

## 快速重传机制
	TCP引入了一种叫Fast Retransmit的算法，不以时间为驱动，而以数据驱动重传。也就是说，如果，包没有连续到达，就ack
最后那个可能被丢了的包，如果发送方连线收到3次相同的ack，就重传。Fast Retransmit的好处就是不用等timeout了再重传。
	比如：如果发送方发出了1，2，3，4，5份数据，第一份先到了，于是就ack回2，结果2因为某些原因没收到，3到达了，于是还是回2，
后面的4和5都到了，但是还是ack回2，因为2还是没有收到，于是发送端收到了三个ack=2的确认，知道了2还没有到，于是就马上重传2。
然后，接收端收到了2，此时因为3、4、5都收到了，于是ack回6
	Fast Retransmit只解决了timeout的问题，它依然面临一个艰难的选择，就是重传之前的一个还是重传所有的问题。对于上面
的示例来说，是重传#2呢还是重传#2\#3\#4\#5呢？因为发送端不清楚这连线的3个ack 2是谁传回来的？也许是发送端发了20份数据，是#6、
#10、#20传来的呢。这样，发送端很有可能要重传从2到20的这堆数据(这就是某些TCP的实际实现)。可见这是一把双刃剑。

## SACK方法
	另外一种更好的方式：Selective Acknowledgment(参看 RFC 2018)，这种方式需要在TCP头里加一个SACK的东西，ACK还是
Fast Retransmit的ACK，SACK则是汇报收到的数据碎版
	




引：
 http://kb.cnblogs.com/page/209100/
 https://tools.ietf.org/html/rfc793
 https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt 官方文档
 http://elixir.free-electrons.com/linux/latest/ident/tcp_timewait_state_process lunix内核代码
 http://tools.ietf.org/html/rfc1122
 https://en.wikipedia.org/wiki/HTTP_persistent_connection
 https://tools.ietf.org/html/rfc2018
 https://www.ibm.com/developerworks/cn/linux/l-tcp-sack/  TCP 选择性应答的性能权衡
 */
